import * as THREE from '../../build/three.module.js';
import Stats from '../../jsm/libs/stats.module.js';

import { GLTFLoader } from '../../jsm/loaders/GLTFLoader.js';
import { DRACOLoader } from '../../jsm/loaders/DRACOLoader.js';
import { RGBELoader } from '../../jsm/loaders/RGBELoader.js';

import { CarControls } from '../../jsm/misc/CarControls.js';

import { PMREMGenerator } from '../../jsm/pmrem/PMREMGenerator.js';
import { PMREMCubeUVPacker } from '../../jsm/pmrem/PMREMCubeUVPacker.js';

// 操作按钮
const bodyMatSelect = document.getElementById('body-mat');
const rimMatSelect = document.getElementById('rim-mat');
const glassMatSelect = document.getElementById('glass-mat');

const followCamera = document.getElementById('camera-toggle');

// 常量
const carParts = {
  body: [],
  rims: [],
  glass: []
};
const damping = 1;

// 全局变量
// 环境贴图, 材质列表，汽车模型
let envMap, materialsLib = {}, carModel;

// 场景
const scene = new THREE.Scene();

// 相机
const camera = new THREE.PerspectiveCamera(40, innerWidth / innerHeight, 0.1, 200);
camera.position.set(3.25, 2, -5);
camera.lookAt(0, 0.25, 0);
// 随车相机
const cameraBack = new THREE.Object3D();
cameraBack.position.set(0, 2.5, 5);
// 相机目标点
const cameraTarget = new THREE.Vector3();

// 渲染器
const renderer = new THREE.WebGLRenderer({ antialias: true });
renderer.setPixelRatio(devicePixelRatio);
renderer.setSize(innerWidth, innerHeight);
document.body.appendChild(renderer.domElement);
renderer.gammaOutput = true;
renderer.toneMapping = THREE.ACESFilmicToneMapping;
renderer.toneMappingExposure = 1; // 色调映射的曝光级别

// 全局时钟
const clock = new THREE.Clock();

// 车辆控制器实例化
const carControls = new CarControls();
carControls.turningRadius = 75; // 转弯半径

// 统计
const stats = new Stats();
document.body.appendChild(stats.dom);

// 地面
const ground = new THREE.Mesh(
  new THREE.PlaneBufferGeometry(400, 400),
  new THREE.MeshBasicMaterial({ color: 0x6e6a62, depthWrite: false })
);
ground.rotation.x = -Math.PI / 2;
ground.renderOrder = 1;
scene.add(ground);
// 地面上的网格标志
const grid = new THREE.GridHelper(400, 8, 0x000000, 0x000000);
grid.material.opacity = 0.2;
grid.material.depthWrite = false;
grid.material.transparent = true;
scene.add(grid);

// 更新汽车材质
const updateMaterials = () => {
  const bodyMat = materialsLib.main[ bodyMatSelect.selectedIndex ];
  const rimMat = materialsLib.main[ rimMatSelect.selectedIndex ];
  const glassMat = materialsLib.glass[ glassMatSelect.selectedIndex ];

  carParts.body.forEach(part => part.material = bodyMat);
  carParts.rims.forEach(part => part.material = rimMat);
  carParts.glass.forEach(part => part.material = glassMat);
}

// 初始化汽车材质选择菜单
const initMaterialSelectionMenus = () => {
  const addOption = (name, mene) => {
    const option = document.createElement('option');
    option.text = name;
    option.value = name;
    mene.add(option);
  }

  materialsLib.main.forEach(material => {
    addOption(material.name, bodyMatSelect);
    addOption(material.name, rimMatSelect);
  })

  materialsLib.glass.forEach(material => {
    addOption(material.name, glassMatSelect);
  });

  // 默认选中项
  bodyMatSelect.selectedIndex = 3;
  rimMatSelect.selectedIndex = 5;
  glassMatSelect.selectedIndex = 0;

  // 选择事件绑定
  bodyMatSelect.addEventListener('change', updateMaterials);
  rimMatSelect.addEventListener('change', updateMaterials);
  glassMatSelect.addEventListener('change', updateMaterials);
}

// 初始化材质
const initMaterials = () => {
  // 车身材质
  materialsLib.main = [
    new THREE.MeshStandardMaterial({
      color: 0xff4400, envMap: envMap, metalness: 1, roughness: 0.2, name: 'orange'
    }),
    new THREE.MeshStandardMaterial( {
      color: 0x001166, envMap: envMap, metalness: 1.0, roughness: 0.2, name: 'blue'
    } ),
    new THREE.MeshStandardMaterial( {
      color: 0x990000, envMap: envMap, metalness: 1.0, roughness: 0.2, name: 'red'
    } ),
    new THREE.MeshStandardMaterial( {
      color: 0x000000, envMap: envMap, metalness: 1.0, roughness: 0.2, name: 'black'
    } ),
    new THREE.MeshStandardMaterial( {
      color: 0xffffff, envMap: envMap, metalness: 0.3, roughness: 0.2, name: 'white'
    } ),
    new THREE.MeshStandardMaterial( {
      color: 0xffffff, envMap: envMap, metalness: 1.0, roughness: 0.2, name: 'metallic'
    } )
  ];
  // 车窗材质
  materialsLib.glass = [
    new THREE.MeshPhysicalMaterial( {
      color: 0xffffff, envMap: envMap, metalness: 1, roughness: 0, transparency: 1.0, transparent: true, name: 'clear'
    } ),
    new THREE.MeshPhysicalMaterial( {
      color: 0x000000, envMap: envMap, metalness: 1, roughness: 0, transparency: 0.7, transparent: true, name: 'smoked'
    } ),
    new THREE.MeshPhysicalMaterial( {
      color: 0x001133, envMap: envMap, metalness: 1, roughness: 0, transparency: 0.7, transparent: true, name: 'blue'
    } )
  ]
}

// 初始化汽车模型
const initCar = () => {
  // 创建 draco 解压器
  const dracoLoader = new DRACOLoader();
  dracoLoader.setDecoderPath('../../js/libs/draco/gltf/');

  const loader = new GLTFLoader();
  loader.setDRACOLoader(dracoLoader);
  loader.load('../../models/gltf/ferrari.glb', gltf => {
    // 获取模型对象
    carModel = gltf.scene.children[0];

    // 设置控制器
    carControls.setModel(carModel);

    carModel.add(cameraBack);

    // 设置模型动态范围贴图 
    carModel.traverse(child => {
      if (child.isMesh) {
        child.material.envMap = envMap;
      }
    });

    // 阴影
    const texture = new THREE.TextureLoader().load('../../models/gltf/ferrari_ao.png');
    const shadow = new THREE.Mesh(
      new THREE.PlaneBufferGeometry(0.655 * 4, 1.3 * 4),
      new THREE.MeshBasicMaterial({ map: texture, transparent: true })
    );
    shadow.rotation.x = -Math.PI / 2;
    shadow.renderOrder = 2;
    carModel.add(shadow);

    scene.add( carModel );

    // 汽车各部分样式选择
    carParts.body.push( carModel.getObjectByName('body') );

    carParts.rims.push(
      carModel.getObjectByName('rim_fl'),
      carModel.getObjectByName('rim_fr'),
      carModel.getObjectByName('rim_rr'),
      carModel.getObjectByName('rim_rl'),
      carModel.getObjectByName('trim'),
    );

    carParts.glass.push(
      carModel.getObjectByName('glass')
    )

    updateMaterials();
  })
}

// 加载搞动态范围环境贴图
new RGBELoader()
  .setDataType(THREE.UnsignedByteType)
  .setPath('../../textures/hdr/')
  .load('quarry_01_1k.hdr', texture => {

    const options = {
      minFilter: texture.minFilter,
      maxFilter: texture.maxFilter
    };

    // 将场景背景设置为加载的环境贴图
    scene.background = new THREE.WebGLRenderTargetCube( 1024, 1024, options ).fromEquirectangularTexture( renderer, texture );

    // 设置全局环境贴图引用
    envMap = scene.background;

    // 预过滤的弧度映射环境贴图生成器
    const pmremGenerator = new PMREMGenerator( scene.background.texture );
    pmremGenerator.update(renderer);

    // 预过滤的弧度映射环境贴图 UV 包裹器
    const pmremCubeUVPacker = new PMREMCubeUVPacker( pmremGenerator.cubeLods );
    pmremCubeUVPacker.update(renderer);

    envMap = pmremCubeUVPacker.CubeUVRenderTarget.texture;

    pmremGenerator.dispose();
    pmremCubeUVPacker.dispose();
    
    initMaterials();
    initMaterialSelectionMenus();
    initCar();
  })

// 主渲染函数
const render = () => {

  const delta = clock.getDelta();

  if (carModel) {
    carControls.update(delta / 3);

    // 只能在以初始点为圆心，半径200范围内移动，超过则复位
    if (carModel.position.length() > 200) {
      carModel.position.set(0, 0, 0);
      carControls.speed = 0;
    }

    if (followCamera.checked) {
      cameraBack.getWorldPosition(cameraTarget); // cameraBack 的世界坐标拷贝到 cameraTarget 中
    } else {
      cameraTarget.set(3.25, 2, -5);
    }

    camera.position.lerp(cameraTarget, delta, damping);

    const position = carModel.position;

    camera.lookAt(position.x, position.y + 0.25, position.z);
  }

  stats.update();
  renderer.render(scene, camera);
}

// 驱动
renderer.setAnimationLoop(render);

// 事件处理
// window resize 事件处理
const handleWindowResize = () => {
  camera.aspect = innerWidth / innerHeight;
  camera.updateProjectionMatrix();

  renderer.setSize(innerWidth, innerHeight);
}

window.addEventListener('resize', handleWindowResize, false);